﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Data;
using System.Xml;

namespace NIS_Downloader
{
    public partial class Form1 : Form
    {
        #region Variables

        const string NUSURL = "http://nus.cdn.shop.wii.com/ccs/download/";
        const string DSiNUSURL = "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/";
        const string certs_MD5 = "7677AD47BAA5D6E3E313E72661FBDC16";
        string version = "1.0";
        WebClient generalWC = new WebClient();

        #endregion

        #region Structures

        public struct WADHeader
        {
            public int HeaderSize;
            public int WadType;
            public int CertChainSize;
            public int Reserved;
            public int TicketSize;
            public int TMDSize;
            public int DataSize;
            public int FooterSize;
        };

        #endregion

        #region Constructeur

        public Form1()
        {
            InitializeComponent();
        }

        #endregion

        #region Fonctions

        private void CreateTitleDirectory(string repertoire)
        {

            if (Directory.Exists(repertoire))
                Directory.Delete(repertoire, true);

            Directory.CreateDirectory(repertoire);
        }

        private long ByteBoundary(int currentlength)
        {
            // Gets the next 0x40 offset.
            long thelength = currentlength - 1;
            long remainder = 1;

            while (remainder != 0)
            {
                thelength += 1;
                remainder = thelength % 0x40;
            }

            //WriteStatus("Initial Size: " + currentlength);
            //WriteStatus("0x40 Size: " + thelength);

            return (long)thelength;
        }

        private void PackWAD(string titleid, string tmdfilename, int contentcount, string[] contentnames, string[] contentsizes, string totaldirectory, string fichier)
        {
            // Directory stuff
            string currentdir = Application.StartupPath;
            if (!(currentdir.EndsWith(@"\")) || !(currentdir.EndsWith(@"/")))
                currentdir += @"\";

            // Create cert file holder
            FileStream certfs = File.OpenRead(currentdir + @"\cert.sys");
            byte[] certsbuf = ReadFully(certfs, 20);
            certfs.Close();

            // Create ticket file holder
            FileStream fs1 = File.OpenRead(totaldirectory + @"\cetk");
            byte[] cetkbuf = ReadFully(fs1, 20);
            fs1.Close();

            // Create tmd file holder
            FileStream fs2 = File.OpenRead(totaldirectory + @"\" + tmdfilename);
            byte[] tmdbuf = ReadFully(fs2, 20);
            fs2.Close();

            // Create wad file
            FileStream wadfs = new FileStream(totaldirectory + fichier, FileMode.Create);

            // Add wad stuffs
            WADHeader wad = new WADHeader();
            wad.HeaderSize = 0x20;
            wad.WadType = 0x49730000;
            wad.CertChainSize = 0xA00;

            // TMDSize is length of buffer.
            wad.TMDSize = tmdbuf.Length;
            // TicketSize is length of cetkbuf.
            wad.TicketSize = cetkbuf.Length;

            // Write cert[] to 0x40.
            wadfs.Seek(0x40, SeekOrigin.Begin);
            wadfs.Write(certsbuf, 0, certsbuf.Length);
            WriteStatus("Cert wrote (0x" + Convert.ToString(64, 16) + ")");

            // Need 64 byte boundary...
            wadfs.Seek(2624, SeekOrigin.Begin);

            // Cert is 2560
            // Write ticket at this point...
            wad.TicketSize = 0x2A4;
            wadfs.Write(cetkbuf, 0, wad.TicketSize);
            WriteStatus("Ticket wrote (0x" + Convert.ToString((wadfs.Length - 0x2A4), 16) + ")");

            // Need 64 byte boundary...
            wadfs.Seek(ByteBoundary(Convert.ToInt32(wadfs.Length)), SeekOrigin.Begin);

            // Write TMD at this point...
            wadfs.Write(tmdbuf, 0, 484 + (contentcount * 36));
            WriteStatus("TMD wrote (0x" + Convert.ToString((wadfs.Length - (484 + (contentcount * 36))), 16) + ")");

            // Preliminary data size of wad file.
            wad.DataSize = 0;

            // Loop n Add contents
            for (int i = 0; i < contentcount; i++)
            {
                // Need 64 byte boundary...
                wadfs.Seek(ByteBoundary(Convert.ToInt32(wadfs.Length)), SeekOrigin.Begin);

                // Create content file holder
                FileStream cont = File.OpenRead(totaldirectory + @"\" + contentnames[i]);
                byte[] contbuf = ReadFully(cont, 20);
                cont.Close();

                wadfs.Write(contbuf, 0, contbuf.Length);

                if (int.Parse(contentsizes[i], System.Globalization.NumberStyles.HexNumber) != contbuf.Length)
                {
                    // Content size mismatch
                    WriteStatus(contentnames[i] + " wrote (0x" + Convert.ToString((wadfs.Length - contbuf.Length), 16) + ") (Mismatch)");
                }
                else
                {
                    WriteStatus(contentnames[i] + " wrote (0x" + Convert.ToString((wadfs.Length - contbuf.Length), 16) + ")");
                }

                wad.DataSize += contbuf.Length;
            }

            wadfs.Seek(0, SeekOrigin.Begin);

            // Write initial part of header
            byte[] start = new byte[8] { 0x00, 0x00, 0x00, 0x20, 0x49, 0x73, 0x00, 0x00 };
            wadfs.Write(start, 0, start.Length);

            //WriteStatus("Initial header wrote");

            // Write CertChainLength
            wadfs.Seek(0x08, SeekOrigin.Begin);
            byte[] chainsize = InttoByteArray(wad.CertChainSize);
            wadfs.Write(chainsize, 0, 4);

            // Write res
            byte[] reserved = new byte[4] { 0x00, 0x00, 0x00, 0x00 };
            wadfs.Seek(0x0C, SeekOrigin.Begin);
            wadfs.Write(reserved, 0, 4);

            // Write ticketsize
            byte[] ticketsize = new byte[4] { 0x00, 0x00, 0x02, 0xA4 };
            wadfs.Seek(0x10, SeekOrigin.Begin);
            wadfs.Write(ticketsize, 0, 4);

            // Write tmdsize
            int strippedtmd = 484 + (contentcount * 36);
            byte[] tmdsize = InttoByteArray(strippedtmd);
            wadfs.Seek(0x14, SeekOrigin.Begin);
            wadfs.Write(tmdsize, 0, 4);

            // Write data size
            wadfs.Seek(0x18, SeekOrigin.Begin);
            byte[] datasize = InttoByteArray(wad.DataSize);
            wadfs.Write(datasize, 0, 4);

            WriteStatus("WAD Created: " + fichier);


            wadfs.Close();
        }

        private byte[] InttoByteArray(int size)
        {
            byte[] b = new byte[4];
            b = BitConverter.GetBytes(size);

            if (BitConverter.IsLittleEndian)
                Array.Reverse(b);

            return b;
        }

        private string[] GetContentNames(byte[] tmdfile, int length)
        {
            string[] contentnames = new string[length];
            int startoffset = 484;

            for (int i = 0; i < length; i++)
            {
                contentnames[i] = MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 1]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 2]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 3])));
                startoffset += 36;
            }

            return contentnames;
        }

        private string[] GetContentSizes(byte[] tmdfile, int length)
        {
            string[] contentsizes = new string[length];
            int startoffset = 492;

            for (int i = 0; i < length; i++)
            {
                contentsizes[i] = MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 1]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 2]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 3]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 4]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 5]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 6]))) +
                    MakeProperLength(ConvertToHex(Convert.ToString(tmdfile[startoffset + 7])));
                contentsizes[i] = TrimLeadingZeros(contentsizes[i]);
                startoffset += 36;
            }

            return contentsizes;
        }

        private byte[] GetContentHashes(byte[] tmdfile, int length)
        {
            byte[] contenthashes = new byte[length * 20];
            int startoffset = 500;

            for (int i = 0; i < length; i++)
            {
                for (int x = 0; x < 20; x++)
                {
                    contenthashes[(i * 20) + x] = tmdfile[startoffset + x];
                }
                startoffset += 36;
            }
            //WriteStatus(DisplayBytes(contenthashes));
            return contenthashes;
        }

        private byte[] GetContentIndices(byte[] tmdfile, int length)
        {
            byte[] contentindices = new byte[length];
            int startoffset = 0x1E9;

            for (int i = 0; i < length; i++)
            {
                contentindices[i] = tmdfile[startoffset];
                startoffset += 36;
            }

            return contentindices;
        }

        private string TrimLeadingZeros(string num)
        {
            int startindex = 0;
            for (int i = 0; i < num.Length; i++)
            {
                if ((num[i] == 0) || (num[i] == '0'))
                    startindex += 1;
                else
                    break;
            }

            return num.Substring(startindex, (num.Length - startindex));
        }

        private string MakeProperLength(string hex)
        {
            if (hex.Length == 1)
                hex = "0" + hex;

            return hex;
        }

        private string ConvertToHex(string decval)
        {
            //Console.Write("Decimal Value: " + decval);

            // Convert text string to unsigned integer
            int uiDecimal = System.Convert.ToInt32(decval);

            string hexval;
            // Format unsigned integer value to hex and show in another textbox
            //Console.Write("Hex Value: " + String.Format("{0:x2}", uiDecimal));
            hexval = String.Format("{0:x2}", uiDecimal);
            return hexval;
        }

        private void InitCertif()
        {
            // Directory stuff
            string currentdir = Application.StartupPath;
            if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)) == false)
                currentdir += Path.DirectorySeparatorChar;

            if (!File.Exists(currentdir + "cert.sys") || !verifyMd5Hash(currentdir + "cert.sys", certs_MD5))
            {
                // Create certs file
                FileStream certsfs = new FileStream(currentdir + @"cert.sys", FileMode.Create);
                DownloadNISFile("0000000100000002", "tmd.289", currentdir, 0, true);
                DownloadNISFile("0000000100000002", "cetk", currentdir, 0, true);

                // Create ticket file holder
                FileStream cetk = File.OpenRead(currentdir + "cetk");
                byte[] cetkbuf = ReadFully(cetk, 20);
                cetk.Close();

                // Create tmd file holder
                FileStream tmd = File.OpenRead(currentdir + "tmd.289");
                byte[] tmdbuf = ReadFully(tmd, 20);
                tmd.Close();

                certsfs.Seek(0, SeekOrigin.Begin);
                certsfs.Write(tmdbuf, 0x628, 0x400);

                certsfs.Seek(0x400, SeekOrigin.Begin);
                certsfs.Write(tmdbuf, 0x328, 0x300);

                certsfs.Seek(0x700, SeekOrigin.Begin);
                certsfs.Write(cetkbuf, 0x2A4, 0x300);

                certsfs.Close();

                if (!verifyMd5Hash(currentdir + "cert.sys", certs_MD5))
                    Erreur("", "");

                File.Delete(currentdir + "cetk");
                File.Delete(currentdir + "tmd.289");
            }
        }

        private static void Erreur(string caption, string message)
        {
            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
            Application.Exit();
        }

        /// <summary>
        /// Reads data from a stream until the end is reached. The
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="stream">The stream to read data from</param>
        /// <param name="initialLength">The initial buffer length</param>
        public static byte[] ReadFully(Stream stream, int initialLength)
        {
            // If we've been passed an unhelpful initial length, just
            // use 32K.
            if (initialLength < 1)
            {
                initialLength = 32768;
            }

            byte[] buffer = new byte[initialLength];
            int read = 0;

            int chunk;
            while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
            {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's
                // any more information
                if (read == buffer.Length)
                {
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if (nextByte == -1)
                    {
                        return buffer;
                    }

                    // Nope. Resize the buffer, put in the byte we've just
                    // read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }

        private void DownloadNISFile(string titleid, string filename, string placementdir, int sizeinbytes, bool iswiititle)
        {
            string nusfileurl;
            if (iswiititle)
                nusfileurl = NUSURL + titleid + @"/" + filename;
            else
                nusfileurl = DSiNUSURL + titleid + @"/" + filename;

            //if (sizeinbytes != 0)
            //    statusbox.Text += " (" + Convert.ToString(sizeinbytes) + " bytes)";

            generalWC.DownloadFile(nusfileurl, placementdir + filename);
        }

        // Verify a hash against a string.
        private static bool verifyMd5Hash(string input, string hash)
        {
            // Hash the input.
            string hashOfInput = getMd5Hash(input);

            // Create a StringComparer an comare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static string getMd5Hash(string input)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            FileStream fs = new FileStream(input, FileMode.Open);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] hash = md5.ComputeHash(fs);
            fs.Close();
            fs.Dispose();
            foreach (byte hex in hash)
            {
                //Returns hash in lower case.
                //To return upper case change “x2″ to “X2″
                sb.Append(hex.ToString("x2"));
            }
            return sb.ToString();
        }

        private void WriteStatus(string Update)
        {
            lblStatut.Text = Update;
        }

        private void ChargerListeWad()
        {
            tvWad.Nodes.Clear();

            // Directory file
            string currentdir = Application.StartupPath;
            if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)) == false)
                currentdir += Path.DirectorySeparatorChar;

            // download nouveau fichier
            try
            {
                //generalWC.DownloadFile("http://mattdef.free.fr/wii/data.txt", currentdir + "data.txt");
                if (!File.Exists(currentdir + "database.xml"))
                    generalWC.DownloadFile("http://mattdef.free.fr/wii/database.xml", currentdir + "database.xml");
            }
            catch (Exception)
            {
                Erreur("Erreur", "An internet connection is required.");
                Application.Exit();
            }

            data.Clear();
            data.ReadXml(currentdir + "database.xml");

            ChargerTreeview();
        }

        private void ChargerTreeview()
        {
            Dictionary<string, string> dicoRegion = new Dictionary<string, string>();
            dicoRegion.Add("0", "41 (ALL/System)");
            dicoRegion.Add("2", "45 (USA NTSC)");
            dicoRegion.Add("4", "4A (Japan)");
            dicoRegion.Add("9", "50 (Europe PAL)");
            string[] regions = null;

            foreach (DataTable table in data.Tables)
            {
                switch (table.TableName)
                {
                    case "IOS":
                        #region IOS
                        TreeNode childIOS = tvWad.Nodes.Add(table.TableName);
                        foreach (DataRow ligne in table.Rows)
                        {
                            TreeNode childVersion = childIOS.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                            childVersion.ToolTipText = ligne["danger"].ToString();
                            string[] versions = ligne["version"].ToString().Split(',');
                            string wad = ligne["name"].ToString() + "-64-v";
                            foreach (string version in versions)
                            {
                                if (version.Trim() != "")
                                {
                                    TreeNode temp = childVersion.Nodes.Add(wad + version);
                                    temp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim(), CheckPath(wad + version.Trim()) };
                                }
                            }
                        }
                        #endregion
                        break;
                    case "SYS":
                        #region SYS
                        TreeNode childSYS = tvWad.Nodes.Add("System");
                        foreach (DataRow ligne in table.Rows)
                        {
                            TreeNode childVersion = childSYS.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                            childVersion.ToolTipText = ligne["danger"].ToString();
                            string[] versions = ligne["version"].ToString().Split(',');
                            regions = ligne["region"].ToString().Split(',');
                            string wad = "v";
                            foreach (string version in versions)
                            {
                                if (version.Trim() != "")
                                {
                                    string vCourte = version.Split(' ')[0].Trim();
                                    TreeNode temp = childVersion.Nodes.Add(wad + version);
                                    temp.Tag = new string[] { ligne["titleID"].ToString(), vCourte, wad + version.Trim() };
                                }
                            }
                            foreach (string region in regions)
                            {
                                if (region.Trim() != "")
                                {
                                    TreeNode temp = childVersion.Nodes.Add(dicoRegion[region.Trim()]);
                                    string titleid = ligne["titleID"].ToString().Replace("XX", "");
                                    string codeRegion = dicoRegion[region.Trim()].Split(' ')[0];
                                    titleid += codeRegion.Trim();
                                    temp.Tag = new string[] { titleid, "", CheckPath(ligne["name"].ToString() + "_" + dicoRegion[region.Trim()].Replace(codeRegion, "").Trim()) };
                                }
                            }
                        }
                        #endregion
                        break;
                    case "VC":
                        #region VC
                        //TreeNode childVC = tvWad.Nodes.Add("Virtual Console");
                        //TreeNode childNES = null;
                        //TreeNode childC64 = null;
                        //TreeNode childNEO = null;
                        //TreeNode childSNES = null;
                        //TreeNode childN64 = null;
                        //TreeNode childARC = null;
                        //TreeNode childSMS = null;
                        //TreeNode childGEN = null;
                        //TreeNode childTG16 = null;
                        //TreeNode childTGCD = null;
                        //foreach (DataRow ligne in table.Rows)
                        //{
                        //    TreeNode temp;

                        //    switch (ligne["type"].ToString())
                        //    {
                        //        case "NES":
                        //            if (childNES == null)
                        //                childNES = childVC.Nodes.Add("Nintendo Entertainment System");
                        //            temp = childNES.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "C64":
                        //            if (childC64 == null)
                        //                childC64 = childVC.Nodes.Add("Commodore 64");
                        //            temp = childC64.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "NEO":
                        //            if (childNEO == null)
                        //                childNEO = childVC.Nodes.Add("Neo Geo");
                        //            temp = childNEO.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "SNES":
                        //            if (childSNES == null)
                        //                childSNES = childVC.Nodes.Add("Super Nintendo Entertainment System");
                        //            temp = childSNES.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "N64":
                        //            if (childN64 == null)
                        //                childN64 = childVC.Nodes.Add("Nintendo 64");
                        //            temp = childN64.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "ARC":
                        //            if (childARC == null)
                        //                childARC = childVC.Nodes.Add("Arcade");
                        //            temp = childARC.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "SMS":
                        //            if (childSMS == null)
                        //                childSMS = childVC.Nodes.Add("Sega Master System");
                        //            temp = childSMS.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "GEN":
                        //            if (childGEN == null)
                        //                childGEN = childVC.Nodes.Add("Sega Genesis (Megadrive)");
                        //            temp = childGEN.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "TG16":
                        //            if (childTG16 == null)
                        //                childTG16 = childVC.Nodes.Add("NEC TurboGrafx-16 (PC-Engine)");
                        //            temp = childTG16.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //        case "TGCD":
                        //            if (childTGCD == null)
                        //                childTGCD = childVC.Nodes.Add("NEC TurboGrafx-CD (PC-Engine CD-ROM)");
                        //            temp = childTGCD.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //            regions = ligne["region"].ToString().Split(',');
                        //            foreach (string version in regions)
                        //            {
                        //                if (version.Trim() != "")
                        //                {
                        //                    TreeNode childTemp = temp.Nodes.Add(dicoRegion[version.Trim()]);
                        //                    childTemp.Tag = new string[] { ligne["titleID"].ToString(), version.Trim() };
                        //                }
                        //            }
                        //            break;
                        //    }
                        //}
                        #endregion
                        break;
                    case "WW":
                        #region WW
                        //TreeNode childWW = tvWad.Nodes.Add(table.TableName);
                        //foreach (DataRow ligne in table.Rows)
                        //{
                        //    TreeNode childVersion = childWW.Nodes.Add(ligne["name"].ToString() + " - " + ligne["titleID"].ToString());
                        //    regions = ligne["region"].ToString().Split(',');
                        //    foreach (string region in regions)
                        //    {
                        //        if (region.Trim() != "")
                        //        {
                        //            TreeNode temp = childVersion.Nodes.Add(dicoRegion[region.Trim()]);
                        //            string titleid = ligne["titleID"].ToString().Replace("XX", "");
                        //            string codeRegion = dicoRegion[region.Trim()].Split(' ')[0];
                        //            titleid += codeRegion.Trim();
                        //            temp.Tag = new string[] { titleid, "", CheckPath(ligne["name"].ToString() + "_" + dicoRegion[region.Trim()].Replace(codeRegion, "").Trim()) };
                        //        }
                        //    }
                        //}
                        #endregion
                        break;
                }
            }
        }

        private string CheckPath(string path)
        {
            path = path.Replace('/', '-');
            path = path.Replace('\\', '-');
            path = path.Replace('|', '-');
            path = path.Replace('<', '-');
            path = path.Replace('>', '-');

            return path;
        }

        #endregion

        #region Evenements

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = "NIS Downloader - " + version;

            // Chargement des WAD
            ChargerListeWad();

            // Initialisation des certifications
            InitCertif();
        }

        private void btDownload_Click(object sender, EventArgs e)
        {
            if (tvWad.SelectedNode != null && tvWad.SelectedNode.Tag != null)
            {
                string[] tag = (string[])tvWad.SelectedNode.Tag;

                if (!NISDownloader.IsBusy)
                {
                    folderBrowserDialog.SelectedPath = Application.StartupPath;
                    if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
                    {
                        btDownload.Enabled = false;
                        btUpdate.Enabled = false;
                        NISDownloader.RunWorkerAsync(new string[] {folderBrowserDialog.SelectedPath, tag[0], tag[1], tag[2]});
                    }
                }
            }
        }

        private void NISDownloader_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            string repertoire = ((string[])e.Argument)[0];
            string version = ((string[])e.Argument)[2];

            // Current directory...
            string currentdir = Application.StartupPath;
            if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)) == false)
                currentdir += Path.DirectorySeparatorChar;

            string titleid = ((string[])e.Argument)[1];
            string wad = ((string[])e.Argument)[3];

            // Set UserAgent to Wii value
            generalWC.Headers.Add("User-Agent", "Opera/9.30 (Nintendo Wii; U; ; 2071; Wii Shop Channel/16.0(A); en)");

            // Get placement directory early...
            string titledirectory;
            titledirectory = repertoire + Path.DirectorySeparatorChar + wad + Path.DirectorySeparatorChar;
            string fichier = wad + ".wad";

            CreateTitleDirectory(titledirectory);

            NISDownloader.ReportProgress(25, "Init...");

            // Download TMD before the rest...
            string tmdfull = "tmd";
            if (version != "")
                tmdfull += "." + version;
            try
            {
                DownloadNISFile(titleid, tmdfull, titledirectory, 0, true);
            }
            catch (Exception)
            {
                NISDownloader.ReportProgress(0, "NIS (404): " + tmdfull);
                Directory.Delete(titledirectory, true);
                return;
            }
            NISDownloader.ReportProgress(50, "Init...");

            // Download CETK after tmd...
            try
            {
                DownloadNISFile(titleid, "cetk", titledirectory, 0, true);
            }
            catch (Exception)
            {
                NISDownloader.ReportProgress(0, "NIS (404): cetk");
                Directory.Delete(titledirectory, true);
                return;
            }
            NISDownloader.ReportProgress(100, "Init...");

            // Obtain TitleKey
            byte[] titlekey = new byte[16];

            // Read the tmd as a stream...
            FileStream fs = File.OpenRead(titledirectory + tmdfull);
            byte[] tmd = ReadFully(fs, 20);

            // Read Title Version...
            string tmdversion = "";
            for (int x = 476; x < 478; x++)
            {
                tmdversion += MakeProperLength(ConvertToHex(Convert.ToString(tmd[x])));
            }
            tmdversion = Convert.ToString(int.Parse(tmdversion, System.Globalization.NumberStyles.HexNumber));

            // Read Content #...
            string contentstrnum = "";
            for (int x = 478; x < 480; x++)
            {
                contentstrnum += TrimLeadingZeros(Convert.ToString(tmd[x]));
            }
            NISDownloader.ReportProgress(0, "Content #: " + contentstrnum);

            // Gather information...
            string[] tmdcontents = GetContentNames(tmd, Convert.ToInt32(contentstrnum));
            string[] tmdsizes = GetContentSizes(tmd, Convert.ToInt32(contentstrnum));
            byte[] tmdhashes = GetContentHashes(tmd, Convert.ToInt32(contentstrnum));
            byte[] tmdindices = GetContentIndices(tmd, Convert.ToInt32(contentstrnum));

            // Progress bar total size tally info...
            float totalcontentsize = 0;
            float currentcontentlocation = 0;
            for (int i = 0; i < tmdsizes.Length; i++)
            {
                totalcontentsize += int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber);
            }

            for (int i = 0; i < tmdcontents.Length; i++)
            {
                try
                {
                    DownloadNISFile(titleid, tmdcontents[i], titledirectory, int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber), true);
                }
                catch (Exception)
                {
                    NISDownloader.ReportProgress(0, "NIS (404): " + tmdcontents[i]);
                    return;
                }

                currentcontentlocation += int.Parse(tmdsizes[i], System.Globalization.NumberStyles.HexNumber);

                NISDownloader.ReportProgress(Convert.ToInt32(((currentcontentlocation / totalcontentsize) * 100)), "Download " + tmdcontents[i]);
            }

            PackWAD(titleid, tmdfull, tmdcontents.Length, tmdcontents, tmdsizes, titledirectory, fichier);

            fs.Close();
            fs.Dispose();

            DirectoryInfo dir = new DirectoryInfo(titledirectory);
            File.Delete(dir.Parent.FullName + Path.DirectorySeparatorChar + fichier);
            File.Move(titledirectory + Path.DirectorySeparatorChar + fichier, dir.Parent.FullName + Path.DirectorySeparatorChar + fichier);
            Directory.Delete(dir.FullName, true);

            NISDownloader.ReportProgress(0, "NIS Download Finished.");
        }

        private void NISDownloader_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            btDownload.Enabled = true;
            btUpdate.Enabled = true;
        }

        private void NISDownloader_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {
            ProgressBar.Value = e.ProgressPercentage;
            WriteStatus(e.UserState.ToString());
        }

        private void btAbout_Click(object sender, EventArgs e)
        {
            Form2 frm = new Form2(version);
            frm.ShowDialog(this);
        }

        private void btUpdate_Click(object sender, EventArgs e)
        {
            btDownload.Enabled = false;
            btUpdate.Enabled = false;

            // Directory file
            string currentdir = Application.StartupPath;
            if (currentdir.EndsWith(Convert.ToString(Path.DirectorySeparatorChar)) == false)
                currentdir += Path.DirectorySeparatorChar;

            try
            {
                //generalWC.DownloadFile("http://mattdef.free.fr/wii/data.txt", currentdir + "data.txt");
                //if (!File.Exists(currentdir + "database.xml"))
                generalWC.DownloadFile("http://mattdef.free.fr/wii/database.xml", currentdir + "newdatabase.xml");
            }
            catch (Exception)
            {
                Erreur("Erreur", "An internet connection is required.");
                Application.Exit();
            }

            DataSet dataTemp = new DataSet();
            dataTemp.ReadXml(currentdir + "newdatabase.xml");

            string newVersion = dataTemp.Tables["database"].Rows[0]["v"].ToString();
            string oldVersion = data.Tables["database"].Rows[0]["v"].ToString();

            if (newVersion == oldVersion)
            {
                MessageBox.Show("Database already up to date.", "Update", MessageBoxButtons.OK, MessageBoxIcon.Information);
                File.Delete(currentdir + "newdatabase.xml");
            }
            else
            {
                File.Delete(currentdir + "database.xml");
                File.Move(currentdir + "newdatabase.xml", currentdir + "database.xml");
                ChargerListeWad();
                MessageBox.Show("Database updated !", "Update", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            btDownload.Enabled = true;
            btUpdate.Enabled = true;
        }

        #endregion
    }
}
